#include "TypicalEnvironment.h"
#include <cmath>
#include <iostream>

const static double PI = 3.1415926535897932f;

#define random(___average, ___bias) (___average+___bias*(2*EcoSystem::random_double()-1))

TypicalEnvironment::TypicalEnvironment(EcoSystem *eco_system) : 
Environment(eco_system)
{
    set_season(SPRING);
    set_weather(SUNNY);
    set_brightness(0);
    set_temperature(25);
    set_relative_humidity(0.75);
    set_rainfall(0.0);
    current_tick = 0;
    spawn_grass_cd = 0;
}
TypicalEnvironment::~TypicalEnvironment()
{

}


void TypicalEnvironment::on_tick()
{
    if (current_tick % DAY_LAST < DAY_LAST / 4 || current_tick % DAY_LAST > DAY_LAST * 3 / 4)
        set_time_night();
    else
        set_time_day();

    update_brightness();
    if (current_tick % DAY_LAST == DAY_LAST / 2)
        switch (current_tick / (YEAR_LAST / 4))
        {
        case SPRING:
            set_season(SPRING);
            spring();
            break;
        case SUMMER:
            set_season(SUMMER);
            break;
        case AUTUMN:
            set_season(AUTUMN);
            autumn();
            break;
        case WINTER:
            set_season(WINTER);
            winter();
            break;
        default:
            std::cout << "Season error\n";
            break;
        }

    try_spawn_grass();

    ++current_tick;
    if (current_tick == YEAR_LAST)
        current_tick = 0;
}
void TypicalEnvironment::render_environment_background(Gdiplus::Graphics *g) const
{
    switch (get_season())
    {
    case SPRING:
        g->Clear(Gdiplus::Color(0xFF00FF7F));
        break;
    case SUMMER:
        g->Clear(Gdiplus::Color(0xFF7CFC00));
        break;
    case AUTUMN:
        g->Clear(Gdiplus::Color(0xFFF0E68C));
        break;
    case WINTER:
        g->Clear(Gdiplus::Color(0xFFFFFAFA));
        break;
    }
}
void TypicalEnvironment::render_environment_effects(Gdiplus::Graphics *g) const
{
    static Gdiplus::Image *night_sunny = Gdiplus::Image::FromFile(L".\\images\\night_sunny.png");
    static Gdiplus::Image *night_cloudy = Gdiplus::Image::FromFile(L".\\images\\night_cloudy.png");
    static Gdiplus::Image *night_rainy = Gdiplus::Image::FromFile(L".\\images\\night_rainy.png");
    static Gdiplus::Image *day_sunny = Gdiplus::Image::FromFile(L".\\images\\day_sunny.png");
    static Gdiplus::Image *day_cloudy = Gdiplus::Image::FromFile(L".\\images\\day_cloudy.png");
    static Gdiplus::Image *day_rainy = Gdiplus::Image::FromFile(L".\\images\\day_rainy.png");

    if (current_tick % DAY_LAST < DAY_LAST / 4 || current_tick % DAY_LAST > DAY_LAST * 3 / 4)
    {
        switch (get_weather())
        {
        case CLOUDY:g->DrawImage(night_cloudy, 0, 0); break;
        case RAINY: g->DrawImage(night_rainy, 0, 0); break;
        case SUNNY:
        default:g->DrawImage(night_sunny, 0, 0); break;
        }
    }
    else
    {
        switch (get_weather())
        {
        case CLOUDY:g->DrawImage(day_cloudy, 0, 0); break;
        case RAINY: g->DrawImage(day_rainy, 0, 0); break;
        case SUNNY:
        default:g->DrawImage(day_sunny, 0, 0); break;
        }
    }
}


void TypicalEnvironment::spring()
{
    using namespace std;
    if (EcoSystem::random_double() < 0.4)
    {
        set_weather(RAINY);
        set_rainfall(random(50.0, 30.0));
        set_relative_humidity(random(0.8, 0.1));
    }
    else
    {
        set_rainfall(0);
        set_relative_humidity(random(0.5, 0.25));
        if (EcoSystem::random_double() < 0.8)
            set_weather(SUNNY);
        else
            set_weather(CLOUDY);
    }

    set_temperature(random(22.0, 6.0));


}
void TypicalEnvironment::summer()
{
    using namespace std;
    if (EcoSystem::random_double() < 0.5)
    {
        set_weather(RAINY);
        set_rainfall(random(300.0, 200.0));
        set_relative_humidity(random(0.9, 0.1));
        set_temperature(random(27.0, 2.0));
    }
    else
    {
        set_rainfall(0);
        set_relative_humidity(random(0.5, 0.25));
        if (EcoSystem::random_double() < 0.8)
        {
            set_weather(SUNNY);
            set_temperature(random(31.0, 1.0));
        }
        else
        {
            set_weather(CLOUDY);
            set_temperature(random(30.0, 3.0));
        }
    }


}
void TypicalEnvironment::autumn()
{
    using namespace std;
    if (EcoSystem::random_double() < 0.1)
    {
        set_weather(RAINY);
        set_rainfall(random(100.0, 80.0));
        set_relative_humidity(random(0.8, 0.1));
    }
    else
    {
        set_rainfall(0);
        set_relative_humidity(random(0.3, 0.2));
        if (EcoSystem::random_double() < 0.6)
            set_weather(SUNNY);
        else
            set_weather(CLOUDY);
    }

    set_temperature(random(15.0, 8.0));

}
void TypicalEnvironment::winter()
{
    using namespace std;
    if (EcoSystem::random_double() < 0.05)
    {
        set_weather(RAINY);
        set_rainfall(random(15.0, 10.0));
        set_relative_humidity(random(0.5, 0.1));
        set_temperature(random(5.0, 3.0));
    }
    else
    {
        set_rainfall(0);
        set_relative_humidity(random(0.3, 0.25));
        if (EcoSystem::random_double() < 0.3)
        {
            set_weather(SUNNY);
            set_temperature(random(10.0, 5.0));
        }
        else
        {
            set_temperature(random(9.0, 5.0));
            set_weather(CLOUDY);
        }
    }


}
void TypicalEnvironment::update_brightness()
{
    using namespace std;

    double brightness = -0.5 * cos(2 * PI / 200.0 * (current_tick % DAY_LAST)) + 0.5;

    if (get_weather() == CLOUDY)
        brightness *= 0.70;
    else if (get_weather() == RAINY)
    {
        double coefficient = get_rainfall() > 300.0 ? 0.5 : (1.0 - get_rainfall() / 600);
        double brightness2 = coefficient * brightness;
        if (brightness2 < 0.5 && brightness > 0.5)
            brightness = 0.5;
        else
            brightness = brightness2;
    }

    set_brightness(brightness);

}
void TypicalEnvironment::try_spawn_grass()
{
    spawn_grass_cd -= (50 * get_brightness() + 10 * get_rainfall() / 100.0)*(-0.5*cos((get_temperature() / 25.0)*PI) + 0.5);
    if (spawn_grass_cd < 0)
    {
        spawn_grass_cd = 200;
        for (int i = 0; i < 100; i++)
            eco_system->spawn_entity(new Grass(eco_system));
    }
}

void TypicalEnvironment::set_current_tick(unsigned int tick)
{
    if (tick < YEAR_LAST)
        current_tick = tick - tick % DAY_LAST + DAY_LAST / 2;
}